/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.beans; import java.beans.Introspector; import java.beans.IntrospectionException; import java.text.MessageFormat; import java.util.ResourceBundle; import java.lang.reflect.Modifier; import org.openide.src.ClassElement; import org.openide.src.MethodElement; import org.openide.src.MethodParameter; import org.openide.src.Type; import org.openide.src.Identifier; import org.openide.src.SourceException; import org.openide.nodes.Node; import org.openide.TopManager; import org.openide.NotifyDescriptor; import org.openide.util.Utilities; import org.openide.util.NbBundle; /** EventSetPattern: This class holds the information about used event set pattern * in code. * @author Petr Hrebejk * * * PENDING: Add Pattern class hierarchy (abstract classes || interfaces ) */ public class EventSetPattern extends Pattern { private static final ResourceBundle bundle = NbBundle.getBundle( EventSetPattern.class ); static final String[] WELL_KNOWN_LISTENERS = new String[] { "java.awt.event.ActionListener", // NOI18N "java.awt.event.ContainerListener", // NOI18N "java.awt.event.FocusListener", // NOI18N "java.awt.event.ItemListener", // NOI18N "java.awt.event.KeyListener", // NOI18N "java.awt.event.MouseListener", // NOI18N "java.awt.event.MouseMotionListener", // NOI18N "java.awt.event.WindowListener", // NOI18N "java.beans.PropertyChangeListener", // NOI18N "java.beans.VetoableChangeListener", // NOI18N "javax.swing.event.CaretListener", // NOI18N "javax.swing.event.ChangeListener", // NOI18N "javax.swing.event.DocumentListener", // NOI18N "javax.swing.event.HyperlinkListener", // NOI18N "javax.swing.event.MenuListener", // NOI18N "javax.swing.event.MouseInputListener", // NOI18N "javax.swing.event.PopupMenuListener", // NOI18N "javax.swing.event.TableColumnModelListener", // NOI18N "javax.swing.event.TableModelListener", // NOI18N "javax.swing.event.TreeModelListener", // NOI18N "javax.swing.event.UndoableEditListener" // NOI18N }; protected MethodElement addListenerMethod = null; protected MethodElement removeListenerMethod = null; private Type type; private boolean isUnicast = false; private ClassElement typeElement; /** holds the decapitalized name */ protected String name; /** Creates new PropertyPattern one of the methods may be null */ public EventSetPattern( PatternAnalyser patternAnalyser, MethodElement addListenerMethod, MethodElement removeListenerMethod ) throws IntrospectionException { super( patternAnalyser ); if ( addListenerMethod == null || removeListenerMethod == null ) throw new InternalError(); this.addListenerMethod = addListenerMethod; this.removeListenerMethod = removeListenerMethod; isUnicast = testUnicast(); findEventSetType(); name = findEventSetName(); typeElement = ClassElement.forName( type.getClassName().getFullName() ) ; } private EventSetPattern( PatternAnalyser patternAnalyser ) { super( patternAnalyser ); } static EventSetPattern create( PatternAnalyser patternAnalyser, String name, String type, boolean isUnicast ) throws SourceException { EventSetPattern esp = new EventSetPattern( patternAnalyser ); esp.name = name; esp.type = Type.parse( type ); esp.isUnicast = isUnicast; esp.generateAddListenerMethod(); esp.generateRemoveListenerMethod(); return esp; } /** Creates new pattern from result of dialog */ static EventSetPattern create( PatternAnalyser patternAnalyser, String type, int implementation, boolean fire, boolean passEvent, boolean isUnicast ) throws SourceException { EventSetPattern esp = new EventSetPattern( patternAnalyser ); esp.type = Type.parse( type ); if ( esp.type == null || !esp.type.isClass() ) { return null; } //System.out.println( "Type " + esp.type.toString() ); // NOI18N esp.name = Introspector.decapitalize( esp.type.getClassName().getName() ); esp.isUnicast = isUnicast; String listenerList = null; if ( implementation == 1 ) { if ( isUnicast ) BeanPatternGenerator.unicastListenerField( esp.getDeclaringClass(), esp.type ); else BeanPatternGenerator.listenersArrayListField( esp.getDeclaringClass(), esp.type ); } else if ( implementation == 2 && !isUnicast ) { listenerList = BeanPatternGenerator.eventListenerListField( esp.getDeclaringClass(), esp.type ); } if ( isUnicast ) { esp.generateAddListenerMethod( BeanPatternGenerator.ucAddBody( esp.type, implementation ), true ); esp.generateRemoveListenerMethod( BeanPatternGenerator.ucRemoveBody( esp.type, implementation ), true ); } else { esp.generateAddListenerMethod( BeanPatternGenerator.mcAddBody( esp.type, implementation, listenerList ), true ); esp.generateRemoveListenerMethod( BeanPatternGenerator.mcRemoveBody( esp.type, implementation, listenerList ), true ); } if ( fire ) { ClassElement listener = ClassElement.forName( type.toString() ); if ( listener != null ) { MethodElement methods[] = listener.getMethods(); for( int i = 0; i < methods.length; i++ ) { if ( (methods[i].getModifiers() & Modifier.PUBLIC) != 0 ) { if ( isUnicast ) BeanPatternGenerator.unicastFireMethod( esp.getDeclaringClass(), esp.type, methods[i], implementation, passEvent ); else BeanPatternGenerator.fireMethod( esp.getDeclaringClass(), esp.type, methods[i], implementation, listenerList, passEvent ); } } } } return esp; } public ClassElement getTypeElement() { return typeElement; } /** Gets the name of PropertyPattern */ public String getName() { return name; } /** Sets the name of PropertyPattern */ public void setName( String name ) throws SourceException { if ( !Utilities.isJavaIdentifier( name ) || name.indexOf( "Listener" ) <= 0 ) // NOI18N throw new SourceException( "Invalid event source name" ); // NOI18N name = capitalizeFirstLetter( name ); Identifier addMethodID = Identifier.create( "add" + name ); //+ "Listener" ); // NOI18N Identifier removeMethodID = Identifier.create( "remove" + name ); //+ "Listener" ); // NOI18N addListenerMethod.setName( addMethodID ); removeListenerMethod.setName( removeMethodID ); this.name = Introspector.decapitalize( name ); } /** Test if the name is valid for given pattern */ protected static boolean isValidName( String str ) { if ( Utilities.isJavaIdentifier(str) == false ) return false; if (str.indexOf( "Listener" ) <= 0 ) // NOI18N return false; return true; } /** Returns the mode of the property READ_WRITE, READ_ONLY or WRITE_ONLY */ public boolean isUnicast() { return isUnicast; } /** Sets the property to be unicast or multicast */ public void setIsUnicast( boolean b ) throws SourceException { if ( b != isUnicast) { Identifier tooMany = Identifier.create( "java.util.TooManyListenersException" ); // NOI18N Identifier[] exs = addListenerMethod.getExceptions(); if (b) { Identifier[] nexs = new Identifier[exs.length + 1]; System.arraycopy( exs, 0, nexs, 0, exs.length ); nexs[ exs.length ] = tooMany; addListenerMethod.setExceptions( nexs ); } else { Identifier[] nexs = new Identifier[exs.length -1]; int found = 0; for( int i = 0; i < exs.length; i++ ) { if ( !exs[i].compareTo( tooMany, false ) ) nexs[i-found] = exs[i]; else found = 1; } addListenerMethod.setExceptions( nexs ); } } this.isUnicast = b; } /** Returns the getter method */ public MethodElement getAddListenerMethod() { return addListenerMethod; } /** Returns the setter method */ public MethodElement getRemoveListenerMethod() { return removeListenerMethod; } /** Gets the type of property */ public Type getType() { return type; } /** Sets the type of property */ public void setType( Type newType ) throws SourceException { if ( newType.compareTo(type, true)) return; //try { //if (!java.util.EventListener.class.isAssignableFrom( newType.toClass() ) ) { if ( !PatternAnalyser.isSubclass( ClassElement.forName( newType.getClassName().getFullName() ), ClassElement.forName( "java.util.EventListener" ) ) ) { // NOI18N TopManager.getDefault().notify( new NotifyDescriptor.Message(PatternNode.bundle.getString("MSG_InvalidListenerInterface"), NotifyDescriptor.ERROR_MESSAGE) ); return; } /* } catch ( java.lang.ClassNotFoundException ex ) { TopManager.getDefault().notify( new NotifyDescriptor.Message(PatternNode.bundle.getString("MSG_ListenerInterfaceNotFound"), NotifyDescriptor.ERROR_MESSAGE) ); return; } */ MethodParameter[] params = addListenerMethod.getParameters(); if ( params.length > 0 ) { params[0].setType( newType ); addListenerMethod.setParameters( params ); } params = removeListenerMethod.getParameters(); if ( params.length > 0 ) { params[0].setType( newType ); removeListenerMethod.setParameters( params ); } // Ask if we have to change the bame of the methods String mssg = MessageFormat.format( PatternNode.bundle.getString( "FMT_ChangeEventSourceName" ), new Object[] { capitalizeFirstLetter( newType.getClassName().getName() ) } ); //new Object[] { "Blah Blah !" } ); // NOI18N NotifyDescriptor nd = new NotifyDescriptor.Confirmation ( mssg, NotifyDescriptor.YES_NO_OPTION ); if ( TopManager.getDefault().notify( nd ).equals( NotifyDescriptor.YES_OPTION ) ) { setName( newType.getClassName().getName() ); } this.type = newType; } /** Gets the cookie of the first available method */ public Node.Cookie getCookie( Class cookieType ) { if ( addListenerMethod != null ) return addListenerMethod.getCookie( cookieType ); if ( removeListenerMethod != null ) return removeListenerMethod.getCookie( cookieType ); return null; } public void destroy() throws SourceException { ClassElement declaringClass; // Remove addListener method if ( addListenerMethod != null ) { declaringClass = addListenerMethod.getDeclaringClass(); if ( declaringClass == null ) { throw new SourceException(); } else { declaringClass.removeMethod( addListenerMethod ); } } // Remove removeListener method if ( removeListenerMethod != null ) { declaringClass = removeListenerMethod.getDeclaringClass(); if ( declaringClass == null ) { throw new SourceException(); } else { declaringClass.removeMethod( removeListenerMethod ); } } } // Utility methods -------------------------------------------------------------------- /* * Package-private constructor * Merge two event set descriptors. Where they conflict, give the * second argument (y) priority over the first argument (x). * * @param x The first (lower priority) EventSetDescriptor * @param y The second (higher priority) EventSetDescriptor */ EventSetPattern( EventSetPattern x, EventSetPattern y) { super( y.patternAnalyser ); //super(x,y); /* listenerMethodDescriptors = x.listenerMethodDescriptors; if (y.listenerMethodDescriptors != null) { listenerMethodDescriptors = y.listenerMethodDescriptors; } if (listenerMethodDescriptors == null) { listenerMethods = y.listenerMethods; } */ addListenerMethod = y.addListenerMethod; removeListenerMethod = y.removeListenerMethod; isUnicast = y.isUnicast; type = y.type; /* if (!x.inDefaultEventSet || !y.inDefaultEventSet) { inDefaultEventSet = false; } */ } /** Finds the Type of property. * @throws IntrospectionException if the property doesnt folow the design patterns */ private void findEventSetType() throws IntrospectionException { if ( addListenerMethod == null ) throw new InternalError( "add method == nul in event set pattern"); // NOI18N type = addListenerMethod.getParameters()[0].getType(); } /** Decides about the name of the event set from names of the methods */ private String findEventSetName() { String compound = addListenerMethod.getName().getName().substring(3); return name = Introspector.decapitalize( compound ); } /** Test if this EventSet pattern is unicast */ private boolean testUnicast() { if (findTooManyListenersException() != null) return true; else return false; } /** @return The identifier for java.util.TooManyListenersException if the addListener * method throws it or null if not. */ Identifier findTooManyListenersException() { Identifier tooMany = Identifier.create( "java.util.TooManyListenersException" ); // NOI18N Identifier[] exs = addListenerMethod.getExceptions(); for ( int i = 0; i < exs.length; i++ ) { if ( exs[i].compareTo( tooMany, false ) ) { return exs[i]; } } return null; } void generateAddListenerMethod () throws SourceException { generateAddListenerMethod( null, false ); } void generateAddListenerMethod ( String body, boolean javadoc ) throws SourceException { ClassElement declaringClass = getDeclaringClass(); MethodElement newMethod = new MethodElement(); MethodParameter[] newParameters = { new MethodParameter( "listener", type, false ) }; // NOI18N newMethod.setName( Identifier.create( "add" + capitalizeFirstLetter( getName() ) ) ); // NOI18N newMethod.setReturn( Type.VOID ); newMethod.setModifiers( Modifier.PUBLIC | Modifier.SYNCHRONIZED ); newMethod.setParameters( newParameters ); if ( declaringClass.isInterface() ) newMethod.setBody( null ); else if ( body != null ) newMethod.setBody( body ); if ( isUnicast ) newMethod.setExceptions( new Identifier[] { Identifier.create( "java.util.TooManyListenersException" ) } ); // NOI18N if ( javadoc ) { String comment = MessageFormat.format( bundle.getString( "COMMENT_AddListenerMethod" ), new Object[] { type.getClassName().getName() } ); newMethod.getJavaDoc().setRawText( comment ); } if ( declaringClass == null ) throw new SourceException(); else { declaringClass.addMethod( newMethod ); addListenerMethod = newMethod; } } void generateRemoveListenerMethod() throws SourceException { generateRemoveListenerMethod( null, false ); } void generateRemoveListenerMethod( String body, boolean javadoc ) throws SourceException { ClassElement declaringClass = getDeclaringClass(); MethodElement newMethod = new MethodElement(); MethodParameter[] newParameters = { new MethodParameter( "listener", type, false ) }; // NOI18N newMethod.setName( Identifier.create( "remove" + capitalizeFirstLetter( getName() ) ) ); // NOI18N newMethod.setReturn( Type.VOID ); newMethod.setModifiers( Modifier.PUBLIC | Modifier.SYNCHRONIZED ); newMethod.setParameters( newParameters ); if ( declaringClass.isInterface() ) newMethod.setBody( null ); else if ( body != null ) newMethod.setBody( body ); if ( javadoc ) { String comment = MessageFormat.format( bundle.getString( "COMMENT_RemoveListenerMethod" ), new Object[] { type.getClassName().getName() } ); newMethod.getJavaDoc().setRawText( comment ); } if ( declaringClass == null ) throw new SourceException(); else { declaringClass.addMethod( newMethod ); removeListenerMethod = newMethod; } } // Property change support ------------------------------------------------------------------------- void copyProperties( EventSetPattern src ) { boolean changed = !src.getType().equals( getType() ) || !src.getName().equals( getName() ) || !(src.isUnicast() == isUnicast()); if ( src.getAddListenerMethod() != addListenerMethod ) addListenerMethod = src.getAddListenerMethod(); if ( src.getRemoveListenerMethod() != removeListenerMethod ) removeListenerMethod = src.getRemoveListenerMethod(); if ( changed ) { isUnicast = testUnicast(); try { findEventSetType(); } catch ( java.beans.IntrospectionException e ) { // Nothing happens } isUnicast = testUnicast(); name = findEventSetName(); firePropertyChange( new java.beans.PropertyChangeEvent( this, null, null, null ) ); } } } /* * Log * 14 Gandalf 1.13 1/13/00 Petr Hrebejk i18n mk3 * 13 Gandalf 1.12 1/12/00 Petr Hrebejk i18n * 12 Gandalf 1.11 1/4/00 Petr Hrebejk Various bugfixes - 5036, * 5044, 5045 * 11 Gandalf 1.10 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 10 Gandalf 1.9 9/13/99 Petr Hrebejk Creating multiple * Properties/EventSet with the same name vorbiden. Forms made i18n * 9 Gandalf 1.8 8/17/99 Petr Hrebejk Combo box with well * known Listener interfaces * 8 Gandalf 1.7 8/2/99 Petr Hrebejk EventSetNode chilfren & * EventSets types with src. code fixed * 7 Gandalf 1.6 7/29/99 Petr Hrebejk Fix - change * ReadOnly/WriteOnly to ReadWrite mode diddn't registered the added * methods properly * 6 Gandalf 1.5 7/26/99 Petr Hrebejk BeanInfo fix & Code * generation fix * 5 Gandalf 1.4 7/26/99 Petr Hrebejk Better implementation of * patterns resolving * 4 Gandalf 1.3 7/21/99 Petr Hrebejk Debug messages removed * 3 Gandalf 1.2 7/21/99 Petr Hrebejk Bug fixes interface * bodies, is for boolean etc * 2 Gandalf 1.1 7/9/99 Petr Hrebejk Factory chaining fix * 1 Gandalf 1.0 6/28/99 Petr Hrebejk * $ */